package br.com.tarlis.persistent.server;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import br.com.tarlis.accesscontrol.client.model.User;
import br.com.tarlis.core.server.exception.UnexpectedException;
import br.com.tarlis.persistent.client.model.Persistent;

public abstract class Factory {

	private static Factory instance;
	
	private PoolManager poolManager;
	private DatabaseConnection<?> actualConnection;
	
	public static Factory getInstance(User user) {
		if (instance == null) {
			throw new UnexpectedException("No databese connected."); //TODO Erro de base de dados
		}
		instance.actualConnection = instance.poolManager.getConnection(user);
		return instance;
	}
	
	protected Factory() {
		Factory.instance = this;
	}
	
	public PoolManager getPoolManager() {
		return poolManager;
	}
	
	public void setPoolManager(PoolManager poolManager) {
		this.poolManager = poolManager;
	}
	
	public DatabaseConnection<?> getActualConnection() {
		return actualConnection;
	}
	
	public void setActualConnection(DatabaseConnection<?> actualConnection) {
		this.actualConnection = actualConnection;
	}
	
	public <K extends Persistent> void insert(K persistent) {
		try {
			actualConnection.startTransaction();
			
			insertImpl(persistent);
			
			actualConnection.commit();
		} catch (UnexpectedException e) {
			throw e;
		} catch (Exception e) {
			throw new UnexpectedException(e);
		}
	}
	
	public <K extends Persistent> void update(K persistent) {
		try {
			actualConnection.startTransaction();
			
			updateImpl(persistent);
			
			actualConnection.commit();
		} catch (UnexpectedException e) {
			throw e;
		} catch (Exception e) {
			throw new UnexpectedException(e);
		}
	}

	public <K extends Persistent> void delete(K persistent) {
		try {
			actualConnection.startTransaction();
			
			deleteImpl(persistent);
			
			actualConnection.commit();
		} catch (UnexpectedException e) {
			throw e;
		} catch (Exception e) {
			throw new UnexpectedException(e);
		}
	}

	public <K extends Persistent> void deleteAll(List<K> list) {
		
		try {
			actualConnection.startTransaction();
			
			for (Persistent persistent : list) {
				deleteImpl(persistent);
			}
			
			actualConnection.commit();
		} catch (UnexpectedException e) {
			throw e;
		} catch (Exception e) {
			throw new UnexpectedException(e);
		}
	}
	
	public <K extends Persistent> List<K> list() {
		return listImpl();
	}
	
	//public <K extends Persistent> List<K> list( ??? );
	
	protected abstract <K extends Persistent> void insertImpl(K persistent) throws UnexpectedException;
	protected abstract <K extends Persistent> void updateImpl(K persistent) throws UnexpectedException;
	protected abstract <K extends Persistent> void deleteImpl(K persistent) throws UnexpectedException;
	protected abstract <K extends Persistent> List<K> listImpl() throws UnexpectedException;
	//protected abstract <K extends Persistent> List<K> listImpl( ??? ) throws UnexpectedException;
	
	protected <K extends Persistent> List<Field> getFields(K persistent) {
		List<Field> fields = new ArrayList<Field>();
		
		Field[] ls = persistent.getClass().getFields();
		
		for (Field f : ls) {
			if (!Modifier.isTransient(f.getModifiers()) && 
				!Modifier.isFinal(f.getModifiers()) &&
				!f.getName().equals("id")) {
				fields.add(f);
			}
		}

		return fields;
	}
	
}
